package pwd.allen.androiddemo.activity;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

import com.alibaba.idst.nui.DateUtil;
import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationListener;
import com.amap.api.maps.AMap;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.LocationSource;
import com.amap.api.maps.MapView;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.MyLocationStyle;
import com.amap.api.services.core.AMapException;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.core.PoiItem;
import com.amap.api.services.geocoder.GeocodeAddress;
import com.amap.api.services.geocoder.GeocodeQuery;
import com.amap.api.services.geocoder.GeocodeResult;
import com.amap.api.services.geocoder.GeocodeSearch;
import com.amap.api.services.geocoder.RegeocodeQuery;
import com.amap.api.services.geocoder.RegeocodeResult;
import com.amap.api.services.poisearch.PoiResult;
import com.amap.api.services.poisearch.PoiSearch;
import com.google.gson.Gson;
import com.tbruyelle.rxpermissions2.Permission;
import com.tbruyelle.rxpermissions2.RxPermissions;

import java.util.Date;

import io.reactivex.functions.Consumer;
import pwd.allen.androiddemo.R;
import pwd.allen.androiddemo.util.ToastUtil;

/**
 * 高德地图
 * 官方文档：https://lbs.amap.com/api/android-sdk/guide/create-map/show-map
 * 官方示例：https://lbs.amap.com/demo/list/sdk#Android
 *
 * 在线api调试：https://lbs.amap.com/demo/javascript-api/example/map/click-to-get-lnglat/
 *
 * TODO 功能
 *  输入地面自动提示 https://lbs.amap.com/api/android-sdk/guide/map-data/poi
 *  导航 https://lbs.amap.com/api/android-navi-sdk/summary/
 *  定位 https://lbs.amap.com/api/android-location-sdk/guide/android-location/getlocation
 *  搜索 https://lbs.amap.com/api/android-sdk/guide/map-data/poi
 *
 *
 * 地理编码:将详细的结构化地址转换为高德经纬度坐标。且支持对地标性名胜景区、建筑物名称解析为高德经纬度坐标结构化地址举例: 北京市朝阳区阜通东大街6号转换后经纬度: 116.480881.39.989410地标性建筑举例: 天安门转换后经结度: 116.397499,39.908722
 * 逆地理编码: 将经结度转换为详细结构化的地址，且返回附近周边的POI、AOI信息例如: 116.480881,39.989410 转换地址描述后: 北京市朝阳区阜通东大街6号
 * */
public class AMapActivity extends AppCompatActivity implements AMapLocationListener, View.OnClickListener, AMap.OnMyLocationChangeListener, LocationSource, GeocodeSearch.OnGeocodeSearchListener, PoiSearch.OnPoiSearchListener {
    public static final int LOCATIONCHOOSE = 200;
    /**定位相关*/
    private AMap aMap;
    private MapView mapView;
    public AMapLocationClient mlocationClient;
    public AMapLocationClientOption mLocationOption = null;

    /**我的地址*/
    private String address;
    /**我的经纬度*/
    private double myLatitude, myLongitude;
    /**地址、时间*/
    private TextView tvBtn, maddress;
    /**是否开启定位*/
    private boolean ifOpen = false;
    /**
     * 定位配置是否使用5.0.0之后的版本
     */
    private boolean ifAfter5 = false;

    private EditText etCont;

    private LocationSource.OnLocationChangedListener onLocationChangedListener;

    /**
     * 地理编码、逆地理编码
     */
    private GeocodeSearch geocoderSearch;

    /**
     * 关键词搜索
     */
    private PoiSearch poiSearch;

    /**
     * 入口
     * @param context
     * @param isRetrunAddress 是否返回地址信息，默认false
     */
    public static void toActivity(Activity context, boolean isRetrunAddress) {
        Intent intent = new Intent(context, AMapActivity.class);
        intent.putExtra("isRetrunAddress", isRetrunAddress);
        context.startActivityForResult(intent, LOCATIONCHOOSE);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a_map);

        getPermission();
        initView();
        initMapView(savedInstanceState);
    }

    private void initView() {
        findViewById(R.id.back).setOnClickListener(this);
        maddress = findViewById(R.id.address);
        tvBtn = findViewById(R.id.tv_btn);
        tvBtn.setOnClickListener(this);
        findViewById(R.id.btn_regeo).setOnClickListener(this);
        findViewById(R.id.btn_geo).setOnClickListener(this);
        findViewById(R.id.btn_poi).setOnClickListener(this);
        etCont = findViewById(R.id.et_cont);
        etCont.setText("39.908888,116.407243");
        etCont.setText("农业银行");
    }

    private void initMapView(Bundle savedInstanceState) {
        // 需要在初始化地图之前 更新同意隐私状态、更新隐私合规状态
        AMapLocationClient.updatePrivacyAgree(getApplicationContext(), true);
        AMapLocationClient.updatePrivacyShow(getApplicationContext(), true, true);

        // 地图初始化
        mapView = (MapView) findViewById(R.id.map);
        //在activity执行onCreate时执行mMapView.onCreate(savedInstanceState)，创建地图
        mapView.onCreate(savedInstanceState);

        //基础地图部分配置
        if (aMap == null) {
            aMap = mapView.getMap();
            // 设置默认定位按钮是否显示，点击按钮会立即进行定位并回调AMap.OnMyLocationChangeListener
            aMap.getUiSettings().setMyLocationButtonEnabled(true);

            // 配置定位蓝点
            MyLocationStyle myLocationStyle = new MyLocationStyle();
            myLocationStyle.interval(2000); //设置连续定位模式下的定位间隔，只在连续定位模式下生效，单次定位模式下不会生效。单位为毫秒。

            //<editor-fold desc="设置蓝点展现模式">
//            myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_SHOW);//只定位一次。
//            myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATE) ;//定位一次，且将视角移动到地图中心点。
//            myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_FOLLOW) ;//连续定位、且将视角移动到地图中心点，定位蓝点跟随设备移动。（1秒1次定位）
//            myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_MAP_ROTATE);//连续定位、且将视角移动到地图中心点，地图依照设备方向旋转，定位点会跟随设备移动。（1秒1次定位）
//            myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);//连续定位、且将视角移动到地图中心点，定位点依照设备方向旋转，并且会跟随设备移动。（1秒1次定位）默认执行此种模式。
            //以下三种模式从5.1.0版本开始提供
//            myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER);//连续定位、蓝点不会移动到地图中心点，定位点依照设备方向旋转，并且蓝点会跟随设备移动。
//            myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_FOLLOW_NO_CENTER);//连续定位、蓝点不会移动到地图中心点，并且蓝点会跟随设备移动。
//            myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_MAP_ROTATE_NO_CENTER);//连续定位、蓝点不会移动到地图中心点，地图依照设备方向旋转，并且蓝点会跟随设备移动。
            //</editor-fold>

            //设置不显示半径圈（颜色透明）
            myLocationStyle.strokeColor(Color.argb(0, 0, 0, 0));// 设置圆形的边框颜色
            myLocationStyle.radiusFillColor(Color.argb(0, 0, 0, 0));// 设置圆形的填充颜色

            aMap.setMyLocationStyle(myLocationStyle);

            // 设置缩放级别
            aMap.moveCamera(CameraUpdateFactory.zoomTo(16));

            // 配置定位监听
            if (ifAfter5) {
                // 5.0.0版本之后
                aMap.addOnMyLocationChangeListener(this);
            } else {
                // 5.0.0版本之前
                aMap.setLocationSource(this);
            }

            // 设置为true表示启动显示定位蓝点，false表示隐藏定位蓝点并不进行定位，默认是false。
            aMap.setMyLocationEnabled(true);
            ifOpen = true;

            // 构造geocoderSearch用于地理编码/逆地理编码
            if (geocoderSearch == null) {
                try {
                    geocoderSearch = new GeocodeSearch(this);
                    geocoderSearch.setOnGeocodeSearchListener(this);
                } catch (AMapException e) {
                    e.printStackTrace();
                }
            }

            // 构造poiSearch用于关键词搜索
            if (poiSearch == null) {
                try {
                    poiSearch = new PoiSearch(this, null);
                    poiSearch.setOnPoiSearchListener(this);
                } catch (AMapException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 激活定位 5.0.0版本之前
     * @param listener
     */
    @Override
    public void activate(OnLocationChangedListener listener) {
        onLocationChangedListener = listener;
        if (mlocationClient == null) {
            //定位部分配置
            try {
                mlocationClient = new AMapLocationClient(this);
                //初始化定位参数
                mLocationOption = new AMapLocationClientOption();
                //设置返回地址信息，默认为true
                mLocationOption.setNeedAddress(true);
                //设置定位模式为高精度模式，Battery_Saving为低功耗模式，Device_Sensors是仅设备模式
                mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
                //设置定位间隔,单位毫秒,默认为2000ms
                mLocationOption.setInterval(2000);
                //设置定位监听
                mlocationClient.setLocationListener(this);
                //设置定位参数
                mlocationClient.setLocationOption(mLocationOption);
                // 此方法为每隔固定时间会发起一次定位请求，为了减少电量消耗或网络流量消耗，
                // 注意设置合适的定位时间的间隔（最小间隔支持为1000ms），并且在合适时间调用stopLocation()方法来取消定位请求
                // 在定位结束后，在合适的生命周期调用onDestroy()方法
                // 在单次定位情况下，定位无论成功与否，都无需调用stopLocation()方法移除请求，定位sdk内部会移除
                //启动定位
                mlocationClient.startLocation();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 停止定位 5.0.0版本之前
     */
    @Override
    public void deactivate() {
        onLocationChangedListener = null;
        if (mlocationClient != null) {
            mlocationClient.stopLocation();
            mlocationClient.onDestroy();
        }
        mlocationClient = null;
    }

    /**
     * 开启或者关闭定位
     */
    private void openOrclose() {
        if (!ifOpen) {
            if (mlocationClient != null) {
                mlocationClient.startLocation();
            } else {
                aMap.setMyLocationEnabled(true);
                // 设置为false再设置true的时候，定位按钮会消失，要重新设置一下
                aMap.getUiSettings().setMyLocationButtonEnabled(true);
            }
            tvBtn.setText("定位开启中");
        } else {
            if (mlocationClient != null) {
                mlocationClient.stopLocation();
            } else {
                aMap.setMyLocationEnabled(false);
            }
            tvBtn.setText("定位已关闭");
        }
        ifOpen = !ifOpen;
    }

    /**
     * 关键词检索POI
     * @param search 关键词
     * @param poiType POI搜索类型，和关键词二者选其一，建议填类型代码而非文字，代码可见：https://lbs.amap.com/api/webservice/download
     * @param cityCode POI搜索区域，可以是城市编码也可以是城市名称，也可以传空字符串，空字符串代表全国在全国范围内进行搜索
     */
    public void poiSearch(String search, String poiType, String cityCode) {
        PoiSearch.Query query = new PoiSearch.Query(search, poiType, cityCode);
        //设置周边搜索的中心点以及半径
        poiSearch.setBound(new PoiSearch.SearchBound(new LatLonPoint(23.111111, 113.111111), 1000));

        // Query官方文档：https://a.amap.com/lbs/static/unzip/Android_Map_Doc/Search/com/amap/api/services/poisearch/PoiSearch.Query.html
        query.setDistanceSort(true);
        query.setExtensions("all"); //  base表示只返回基础数据，all表示所有数据 ，默认 base
        query.setPageNum(1);
        query.setPageSize(10);
        poiSearch.setQuery(query);
        // 发送请求
        poiSearch.searchPOIAsyn();
    }

    /**
     * 定位监听 5.0.0版本之前
     * @param aMapLocation
     */
    @Override
    public void onLocationChanged(AMapLocation aMapLocation) {
        if (aMapLocation != null) {
            if (aMapLocation.getErrorCode() == 0) {
                // 在定位回调中设置显示定位小蓝点，不调用的话地图没有蓝点
                onLocationChangedListener.onLocationChanged(aMapLocation);

                myLatitude = aMapLocation.getLatitude();
                myLongitude = aMapLocation.getLongitude();

                address = aMapLocation.getAddress();
                maddress.setText(String.format("[%s]%s", DateUtil.dateFormat(new Date(), "HH:mm:ss"), address));

                Log.i("gaodeditu", String.format("address:%s, latitude:%s", address, myLatitude));

//                //定位成功回调信息，设置相关消息
//                aMapLocation.getLocationType();//获取当前定位结果来源，如网络定位结果，详见定位类型表
//                aMapLocation.getLatitude();//获取纬度
//                aMapLocation.getLongitude();//获取经度
//                aMapLocation.getAccuracy();//获取精度信息
//                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//                Date date = new Date(aMapLocation.getTime());
//                df.format(date);//定位时间
//                aMapLocation.getAddress();//地址，如果option中设置isNeedAddress为false，则没有此结果，网络定位结果中会有地址信息，GPS定位不返回地址信息。
//                aMapLocation.getCountry();//国家信息
//                aMapLocation.getProvince();//省信息
//                aMapLocation.getCity();//城市信息
//                aMapLocation.getDistrict();//城区信息
//                aMapLocation.getStreet();//街道信息
//                aMapLocation.getStreetNum();//街道门牌号信息
//                aMapLocation.getCityCode();//城市编码
//                aMapLocation.getAdCode();//地区编码
//                onLocationChangedListener.onLocationChanged(aMapLocation);
            } else {
                //显示错误信息ErrCode是错误码，errInfo是错误信息，详见错误码表。
                Log.e("AmapError", String.format("location Error, ErrCode:%s, errInfo:%s", aMapLocation.getErrorCode(), aMapLocation.getErrorInfo()));
            }
        }
    }

    /**
     * 定位监听 5.0.0版本后
     * 获取经纬度信息
     * @param aMapLocation
     */
    @Override
    public void onMyLocationChange(Location aMapLocation) {
        if (aMapLocation != null) {
            myLatitude = aMapLocation.getLatitude();
            myLongitude = aMapLocation.getLongitude();
            Log.i("gaodeditu", String.format("OnMyLocationChangeListener.onMyLocationChange->latitude:%s, longitude:%s", myLatitude, myLongitude));
            // 通过aMap.getMyLocation也能获取当前的定位地址
//            Log.i("gaodeditu", String.format("aMap.getMyLocation->latitude:%s, longitude:%s", aMap.getMyLocation().getLatitude(), aMap.getMyLocation().getLongitude()));

            // 逆地理编码获取地址信息
            regeoSearch(myLatitude, myLongitude);
        }
    }

    /**
     * 定位到 广州塔 ：113.323546,23.106457
     */
    public void location(double latitude, double longitude) {
        //根据获取的经纬度，将地图移动到定位位置
        aMap.moveCamera(CameraUpdateFactory.changeLatLng(new LatLng(latitude, longitude)));
    }

    /**
     * 发起逆地理编码，成功后会回调接口 onRegeocodeSearched 解析返回的结果
     * @param latitude
     * @param longitude
     */
    public void regeoSearch(double latitude, double longitude) {
        // 第一个参数表示一个Latlng，第二参数表示范围多少米，第三个参数表示是火系坐标系还是GPS原生坐标系
        RegeocodeQuery query = new RegeocodeQuery(new LatLonPoint(latitude, longitude), 200,GeocodeSearch.AMAP);
        geocoderSearch.getFromLocationAsyn(query);
    }

    /**
     * 发起地理编码，成功后会回调接口 onGeocodeSearched 解析返回的结果
     * @param address
     */
    public void geoSearch(String address) {
        // name表示地址，第二个参数表示查询城市，中文或者中文全拼，citycode、adcode
        GeocodeQuery query = new GeocodeQuery(address, "广州市");
        geocoderSearch.getFromLocationNameAsyn(query);
    }

    @Override
    public void onClick(View v) {
        String content = null;
        switch (v.getId()) {
            case R.id.back:
                /**返回*/
                this.finish();
                break;
            case R.id.tv_btn:
                this.openOrclose();
                break;
            case R.id.btn_regeo:
                content = etCont.getText().toString();
                if (!content.matches("-?\\d+\\.\\d*,-?\\d+\\.\\d*")) {
                    ToastUtil.show(this, "输入内容格式应为：latitude,longitude");
                    return;
                }
                String[] split = content.split(",");
                double latitude = Double.parseDouble(split[0]);
                double longitude = Double.parseDouble(split[1]);
                regeoSearch(latitude, longitude);
                location(latitude, longitude);
                break;
            case R.id.btn_geo:
                content = etCont.getText().toString();
                geoSearch(content);
                break;
            case R.id.btn_poi:
                content = etCont.getText().toString();
                poiSearch(content, "", "");
                break;
            default:
                break;
        }
    }


    /**获取权限*/
    private void getPermission() {
        RxPermissions rxPermissions = new RxPermissions(this);
        rxPermissions.requestEach(Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_FINE_LOCATION)
                .subscribe(new Consumer<Permission>() {
                    @Override
                    public void accept(Permission permission) throws Exception {
                        if (permission.granted) {
                            // 用户已经同意该权限（该方法每同意一个权限都会执行一次）
                        } else if (permission.shouldShowRequestPermissionRationale) {
                            // 用户拒绝了该权限，没有选中『不再询问』（Never ask again）,那么下次再次启动时，还会提示请求权限的对话框
                        } else {
                            // 用户拒绝了该权限，并且选中『不再询问』
                        }
                    }
                });
    }

    /**
     * 逆地理编码回调函数
     * @param regeocodeResult
     * @param i 响应码;1000表示成功
     */
    @Override
    public void onRegeocodeSearched(RegeocodeResult regeocodeResult, int i) {
        if (i == 1000) {
            if (regeocodeResult != null && regeocodeResult.getRegeocodeAddress() != null) {
                address = regeocodeResult.getRegeocodeAddress().getFormatAddress();
                maddress.setText(String.format("[%s]%s", DateUtil.dateFormat(new Date(), "HH:mm:ss"), address));
                Log.i("gaodeditu", String.format("逆地理编码结果-》address:%s, building:%s", address, regeocodeResult.getRegeocodeAddress().getBuilding()));
            }
        } else {
            Log.e("AmapError", String.format("逆地理编码失败, ErrCode:%s", i));
        }
    }

    /**
     * 地理编码回调函数
     * @param geocodeResult
     * @param i
     */
    @Override
    public void onGeocodeSearched(GeocodeResult geocodeResult, int i) {
        if (i == 1000) {
            if (geocodeResult != null && geocodeResult.getGeocodeAddressList().size() > 0) {
                for (GeocodeAddress geocodeAddress : geocodeResult.getGeocodeAddressList()) {
                    location(geocodeAddress.getLatLonPoint().getLatitude(), geocodeAddress.getLatLonPoint().getLongitude());
                    String desc = String.format("地理编码结果-》address:%s, latitude:%s, longitude:%s", geocodeAddress.getFormatAddress(), geocodeAddress.getLatLonPoint().getLatitude(), geocodeAddress.getLatLonPoint().getLongitude());
                    ToastUtil.show(this, desc);
                    Log.i("gaodeditu", desc);
                }
            }
        } else {
            Log.e("AmapError", String.format("逆地理编码失败, ErrCode:%s", i));
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        //在activity执行onResume时执行mMapView.onResume ()，重新绘制加载地图
        mapView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        //在activity执行onPause时执行mMapView.onPause ()，暂停地图的绘制
        mapView.onPause();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //在activity执行onSaveInstanceState时执行mMapView.onSaveInstanceState (outState)，保存地图当前的状态
        mapView.onSaveInstanceState(outState);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mlocationClient != null) {
            mlocationClient.onDestroy();
        }
        //在activity执行onDestroy时执行mMapView.onDestroy()，销毁地图
        mapView.onDestroy();
    }

    @Override
    public void onPoiSearched(PoiResult poiResult, int i) {
        if (i == 1000) {
            if (poiResult != null && poiResult.getPois().size() > 0) {
                Log.i("gaodeditu", String.format("总页数:%s", poiResult.getPageCount()));
                for (PoiItem pois : poiResult.getPois()) {
                    Log.i("gaodeditu", new Gson().toJson(pois));
                    Log.i("gaodeditu", String.format("poi检索结果-》title:%s, snippet:%s, latitude:%s，longitude:%s", pois.getTitle(), pois.getSnippet(), pois.getLatLonPoint().getLatitude(), pois.getLatLonPoint().getLongitude()));
                }
            }
        } else {
            Log.e("AmapError", String.format("poi检索失败, ErrCode:%s", i));
        }
    }

    /**
     * ID检索POI时的回调函数
     * @param poiItem
     * @param i
     */
    @Override
    public void onPoiItemSearched(PoiItem poiItem, int i) {

    }
}
